package JavaUtils.uitl;


import JavaUtils.CSUtils.Log.CLog;
import JavaUtils.uitl.math.*;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;


public class ByteStream {
    static final String tag = "ByteStream";
    protected static final int DEFAULT_CAPACITY = 10;
    protected byte[] buf;
    protected int readerIndex;
    protected int writerIndex;

    /**
     * >=0表示index，-1表示空闲状态
     */
    int _tag = -1;

    public ByteStream() {
        this.buf = new byte[DEFAULT_CAPACITY];
    }

    public ByteStream(int capacity) {
        this.buf = new byte[capacity];
    }

    public int getTag() {
        return _tag;
    }

    public void setTag(int tag) {
        this._tag = tag;
    }

    public ByteStream(byte[] b) {
        this.buf = b;
        this.writerIndex = this.buf.length;
    }

    public void setBytes(byte[] bytes) {
        buf = bytes;
        writerIndex = bytes.length;
        readerIndex = 0;
    }

    public void born() {
        if (buf.length > 1024) {
            buf = new byte[DEFAULT_CAPACITY];
        }
    }

    public void reset() {
        this.writerIndex = 0;
        this.readerIndex = 0;
        _tag = -1;
    }

    public void resetReaderIndex() {
        this.readerIndex = 0;
    }

    public void resetWriterIndex() {
        this.writerIndex = 0;
    }

    public void setWriterIndex(int writeIndex) {
        this.writerIndex = writeIndex;
    }

    public int readerIndex() {
        return readerIndex;
    }

    public int writerIndex() {
        return writerIndex;
    }

    protected void setReaderIndex(int readerIndex) {
        if (readerIndex < 0 || readerIndex > writerIndex)
            throw new IndexOutOfBoundsException();
        this.readerIndex = readerIndex;
    }

    protected void checkReadable(int count) {
        if (writerIndex - readerIndex < count)
            throw new IndexOutOfBoundsException();
    }

    protected void expand(int count) {
        if (count < 0)
            throw new IllegalArgumentException();
        int capacity = writerIndex + count;
        if (buf.length < capacity) {
            capacity = Integer.highestOneBit(capacity);
            capacity <<= 1;
            if (capacity < 0) {
                capacity = Integer.MAX_VALUE;
            }
            byte[] newBuf = new byte[capacity];
            System.arraycopy(buf, 0, newBuf, 0, buf.length);
            buf = newBuf;
        }
    }

    public byte[] getBytes() {
        byte[] bytes = new byte[writerIndex];
        System.arraycopy(buf, 0, bytes, 0, writerIndex - 1);
        return bytes;
    }

    public void write(int b) {
        expand(1);
        this.buf[this.writerIndex++] = (byte) b;
    }

    public void write(int index, int b) {
        if (index < 0)
            throw new IllegalArgumentException();
        if (index + 1 > this.writerIndex)
            throw new IllegalArgumentException();
        this.buf[index] = (byte) b;
    }

    public void write(byte[] b, int off, int len) {
        expand(len);
        System.arraycopy(b, off, this.buf, writerIndex, len);
        this.writerIndex += len;
    }


    public void write(byte[] b) {
        write(b, 0, b.length);
    }

    public void writeBoolean(boolean v) {
        write(v ? 1 : 0);
    }

    public void writeByte(int v) {
        write(v);
    }

    public void writeChar(int v) {
        expand(2);
        this.buf[this.writerIndex++] = (byte) ((v >>> 8) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v) & 0xFF);
    }

    public void writeShort(int v) {
        expand(2);
        this.buf[this.writerIndex++] = (byte) ((v >>> 8) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v) & 0xFF);
    }

    public void writeShort(int index, int v) {
        if (index < 0)
            throw new IllegalArgumentException();
        if (index + 2 > this.writerIndex)
            throw new IllegalArgumentException();
        this.buf[index] = (byte) ((v >>> 8) & 0xFF);
        this.buf[index + 1] = (byte) ((v) & 0xFF);
    }

    public void writeInt(int v) {
        expand(4);
        this.buf[this.writerIndex++] = (byte) ((v >>> 24) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 16) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 8) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v) & 0xFF);
    }

    public void writeInt(int index, int v) {
        if (index < 0)
            throw new IllegalArgumentException();
        if (index + 4 > this.writerIndex)
            throw new IllegalArgumentException();
        this.buf[index++] = (byte) ((v >>> 24) & 0xFF);
        this.buf[index++] = (byte) ((v >>> 16) & 0xFF);
        this.buf[index++] = (byte) ((v >>> 8) & 0xFF);
        this.buf[index] = (byte) ((v) & 0xFF);
    }

    public void writeLong(long v) {
        expand(8);
        this.buf[this.writerIndex++] = (byte) ((v >>> 56) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 48) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 40) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 32) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 24) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 16) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 8) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v) & 0xFF);
    }

    public void writeLong(int index, long v) {
        if (index < 0)
            throw new IllegalArgumentException();
        if (index + 8 > this.writerIndex)
            throw new IllegalArgumentException();
        this.buf[index++] = (byte) ((v >>> 56) & 0xFF);
        this.buf[index++] = (byte) ((v >>> 48) & 0xFF);
        this.buf[index++] = (byte) ((v >>> 40) & 0xFF);
        this.buf[index++] = (byte) ((v >>> 32) & 0xFF);
        this.buf[index++] = (byte) ((v >>> 24) & 0xFF);
        this.buf[index++] = (byte) ((v >>> 16) & 0xFF);
        this.buf[index++] = (byte) ((v >>> 8) & 0xFF);
        this.buf[index] = (byte) ((v) & 0xFF);
    }

    public void writeFloat(float v) {
        writeInt(Float.floatToIntBits(v));
    }

    /**
     * 仅用于-1到1之间的浮点数 ，精度损失较大，慎用
     */
    public void writeFloat8(float v) {
        writeShort(FloatUtils.compress8(v));
    }

    public void writeFloat16(float v) {
        writeShort(FloatUtils.compress16(v));
    }

    public void writeFloat(int index, float v) {
        writeInt(index, Float.floatToIntBits(v));
    }

    public void writeDouble(double v) {
        writeLong(Double.doubleToLongBits(v));
    }

    //都是英文字母，不涉及到编码
    public void writeKey(String str) {
        int len = str.length();
        if (len > 255) {
            throw new IllegalArgumentException();
        }

        byte[] bytearr = new byte[len + 1];
        bytearr[0] = (byte) (len & 0xFF);
        for (int i = 0; i < len; i++) {
            bytearr[i + 1] = (byte) (str.charAt(i) & 0xFF);
        }

        write(bytearr, 0, bytearr.length);
    }

    public void writeString(String str) {
        int strlen = str.length();
        int utflen = 0;
        int c, count = 0;

        for (int i = 0; i < strlen; i++) {
            c = str.charAt(i);
            if ((c >= 0x0001) && (c <= 0x007F)) {
                utflen++;
            } else if (c > 0x07FF) {
                utflen += 3;
            } else {
                utflen += 2;
            }
        }

        if (utflen > 65535)
            throw new IllegalArgumentException();

        byte[] bytearr = new byte[utflen + 2];

        bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
        bytearr[count++] = (byte) ((utflen) & 0xFF);

        int i = 0;
        for (i = 0; i < strlen; i++) {
            c = str.charAt(i);
            if (!((c >= 0x0001) && (c <= 0x007F)))
                break;
            bytearr[count++] = (byte) c;
        }

        for (; i < strlen; i++) {
            c = str.charAt(i);
            if ((c >= 0x0001) && (c <= 0x007F)) {
                bytearr[count++] = (byte) c;

            } else if (c > 0x07FF) {
                bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
                bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F));
                bytearr[count++] = (byte) (0x80 | ((c) & 0x3F));
            } else {
                bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
                bytearr[count++] = (byte) (0x80 | ((c) & 0x3F));
            }
        }
        write(bytearr, 0, utflen + 2);
    }

    public void writeVector3f(Vector3f value) {
        writeFloat(value.x);
        writeFloat(value.y);
        writeFloat(value.z);
    }

    public Vector3f readVector3f(Vector3f v) {
        if (v == null) {
            v = new Vector3f(readFloat(), readFloat(), readFloat());
        } else {
            v.set(readFloat(), readFloat(), readFloat());
        }
        return v;
    }

    public void writeVector2f(Vector2f value) {
        writeFloat(value.x);
        writeFloat(value.y);
    }

    public Vector2f readVector2f(Vector2f v) {
        if (v == null) {
            v = new Vector2f(readFloat(), readFloat());
        } else {
            v.set(readFloat(), readFloat());
        }
        return v;
    }

    public void read(byte[] b) {
        checkReadable(b.length);
        System.arraycopy(this.buf, readerIndex, b, 0, b.length);
        this.readerIndex += b.length;
    }

    public void read(byte[] b, int off, int len) {
        checkReadable(len);
        System.arraycopy(this.buf, readerIndex, b, off, len);
        this.readerIndex += len;
    }

    public void skilBytes(int n) {
        checkReadable(n);
        this.readerIndex += n;
    }

    public boolean readBoolean() {
        checkReadable(1);
        return this.buf[this.readerIndex++] == 0 ? false : true;
    }

    public byte readByte() {
        checkReadable(1);
        return this.buf[this.readerIndex++];
    }

    public char readChar() {
        checkReadable(2);
        return (char) (((this.buf[this.readerIndex++] & 0xFF << 8)) + (this.buf[this.readerIndex++] & 0xFF));
    }

    public int readUnsignedByte() {
        checkReadable(1);
        return this.buf[this.readerIndex++] & 0xFF;
    }

    public short readShort() {
        checkReadable(2);
        return (short) (((this.buf[this.readerIndex++] & 0xFF) << 8) + (this.buf[this.readerIndex++] & 0xFF));
    }

    public int readUnsignedShort() {
        checkReadable(2);
        return (((this.buf[this.readerIndex++] & 0xFF) << 8) + (this.buf[this.readerIndex++] & 0xFF));
    }

    public int readInt() {
        checkReadable(4);
        return ((this.buf[this.readerIndex++] & 0xFF) << 24) + ((this.buf[this.readerIndex++] & 0xFF) << 16) + ((this.buf[this.readerIndex++] & 0xFF) << 8) + ((this.buf[this.readerIndex++] & 0xFF));
    }

    public long readUnsignedInt() {
        return readInt() & 0xFFFFFFFFL;
    }

    public long readLong() {
        checkReadable(8);
//		return (long)((this.buf[this.readerIndex++] & 0xFF) << 56)
//		+ (long)((this.buf[this.readerIndex++] & 0xFF) << 48)
//		+ (long)((this.buf[this.readerIndex++] & 0xFF) << 40)
//		+ (long)((this.buf[this.readerIndex++] & 0xFF) << 32)
//		+ (long)((this.buf[this.readerIndex++] & 0xFF) << 24)
//		+ (long)((this.buf[this.readerIndex++] & 0xFF) << 16)
//		+ (long)((this.buf[this.readerIndex++] & 0xFF) << 8)
//		+ (long)((this.buf[this.readerIndex++] & 0xFF) << 0);

        return (((long) this.buf[this.readerIndex++] << 56)
                + ((long) (this.buf[this.readerIndex++] & 0xFF) << 48)
                + ((long) (this.buf[this.readerIndex++] & 0xFF) << 40)
                + ((long) (this.buf[this.readerIndex++] & 0xFF) << 32)
                + ((long) (this.buf[this.readerIndex++] & 0xFF) << 24)
                + ((this.buf[this.readerIndex++] & 0xFF) << 16)
                + ((this.buf[this.readerIndex++] & 0xFF) << 8)
                + ((this.buf[this.readerIndex++] & 0xFF)));
    }

    public float readFloat() {
        return Float.intBitsToFloat(readInt());
    }

    /**
     * 仅用于-1到1之间的浮点数 ，精度损失较大，慎用
     */
    public float readFloat8() {
        return FloatUtils.decompress8(readByte());
    }

    public float readFloat16() {
        return FloatUtils.decompress16(readShort());
    }

    public double readDouble() {
        return Double.longBitsToDouble(readLong());
    }

    //都是英文字母，不涉及到编码
    public String readKey() {
        int len = readUnsignedByte();
        byte[] bytes = new byte[len];
        if (len > 0) {
            read(bytes);
        }
        return new String(bytes);
    }

    public String readString() {
        int utflen = readUnsignedShort();
        byte[] bytearr = new byte[utflen];
        char[] chararr = new char[utflen];
        int c, char2, char3;
        int count = 0;
        int chararr_count = 0;

        read(bytearr);

        while (count < utflen) {
            c = (int) bytearr[count] & 0xff;
            if (c > 127)
                break;
            count++;
            chararr[chararr_count++] = (char) c;
        }

        while (count < utflen) {
            c = (int) bytearr[count] & 0xff;
            switch (c >> 4) {
                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                    count++;
                    chararr[chararr_count++] = (char) c;
                    break;
                case 12:
                case 13:
                    count += 2;
                    if (count > utflen)
                        throw new RuntimeException();
                    char2 = (int) bytearr[count - 1];
                    if ((char2 & 0xC0) != 0x80)
                        throw new RuntimeException();
                    chararr[chararr_count++] = (char) (((c & 0x1F) << 6) | (char2 & 0x3F));
                    break;
                case 14:
                    count += 3;
                    if (count > utflen)
                        throw new RuntimeException();
                    char2 = (int) bytearr[count - 2];
                    char3 = (int) bytearr[count - 1];
                    if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
                        throw new RuntimeException();
                    char ch = (char) (((c & 0x0F) << 12)
                            | ((char2 & 0x3F) << 6) | ((char3 & 0x3F)));
                    /*0xD800-0xDFFF为UNICODE编码保留区*/
                    if (ch >= 0xD800 && ch <= 0xDFFF) {
                    } else {
                        chararr[chararr_count++] = ch;
                    }
                    break;
                default:
                    throw new RuntimeException();
            }
        }
        return new String(chararr, 0, chararr_count);
    }

    public int size() {
        return writerIndex;
    }

    public byte[] toArray() {
        return Arrays.copyOf(this.buf, writerIndex);
    }
//
//    public int writeToBuf(int count, ByteBuf out, short service) throws Exception {
//
//        if (count > 255) {
//            new IllegalArgumentException();
//        }
//
//        //包长度
//        byte[] len = new byte[7];
//        int length = writerIndex + 3;
//        len[0] = (byte) ((length >>> 24) & 0xFF);
//        len[1] = (byte) ((length >>> 16) & 0xFF);
//        len[2] = (byte) ((length >>> 8) & 0xFF);
//        len[3] = (byte) ((length) & 0xFF);
//
//        //servcie：协议号
//        len[4] = (byte) ((service >>> 8) & 0xFF);
//        len[5] = (byte) ((service) & 0xFF);
//
//        //协议第一层key数量
//        len[6] = (byte) ((count) & 0xFF);
//        out.writeBytes(len, 0, len.length);
//
//        out.writeBytes(buf, 0, writerIndex);
//
//        return writerIndex + len.length;
//    }

    public void writeByteStream(ByteStream other) {
        write(other.buf, other.readerIndex, other.writerIndex - other.readerIndex);
    }

    public byte[] buf() {
        return buf;
    }

    //ybai, 用于保存数据库中的数据
    public void writeBytes(byte[] value) {
        writeInt(value.length);
        if (value.length > 0) {
            write(value, 0, value.length);
        }
    }

    //ybai, 用于保存数据库中的数据
    public byte[] readBytes() {
        int len = readInt();
        byte[] bytes = new byte[len];
        if (len > 0) {
            read(bytes);
        }
        return bytes;
    }

    public static void main(String[] args) throws IOException {
        ByteStream stream = new ByteStream();
        stream.writeInt(123);
        stream.writeFloat(1.234f);
        stream.writeByte(255);
        stream.writeByte(255);
        stream.writeShort(40000);
        stream.writeShort(40000);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        dos.writeInt(222);
        dos.writeFloat(2.222f);
        stream.write(baos.toByteArray());

        CLog.i(tag,stream.readInt());
        CLog.i(tag,stream.readFloat());
        CLog.i(tag,stream.readByte());
        CLog.i(tag,stream.readUnsignedByte());
        CLog.i(tag,stream.readShort());
        CLog.i(tag,stream.readUnsignedShort());
        CLog.i(tag,stream.readInt());
        CLog.i(tag,stream.readFloat());
    }
}

